/*
 * @Author: xiaosihan 
 * @Date: 2023-02-21 09:55:52 
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2024-01-20 21:58:20
 */

import { throttle } from 'lodash';
import { Group, InstancedMesh, Material, Matrix4, Mesh, Vector3, Color } from 'three'
import threeUtils from 'three-base/threeUtils';

// 模型组转成 实例对象
export default class InstancedGroupMesh extends Mesh {

    url = "";

    isInstancedGroupMesh = true

    count: number = 0

    size = new Vector3(1, 1, 1);

    meshCollect: Record<string, Mesh[]> = {}

    instanceCollect: Record<string, InstancedMesh> = {}

    colorMap = new Map<String, Color>();

    declare children: InstancedMesh[];

    static matrix = new Matrix4()

    constructor(group: Group, count: number) {
        super()

        this.size.copy(threeUtils.getSize(group));

        this.count = count

        const { meshCollect, instanceCollect } = this

        group.updateMatrixWorld(true);

        // collect all mesh counts, in case there are more than one copy of the same mesh in this group
        group.traverse((obj) => {
            const mesh = obj as Mesh
            if (mesh.isMesh) {

                mesh.matrixWorld.decompose(mesh.position, mesh.quaternion, mesh.scale);
                mesh.rotation.setFromQuaternion(mesh.quaternion);
                mesh.updateMatrix();

                // same geometry and same material consider as the same mesh
                const uuid = mesh.geometry.uuid + '/' + (mesh.material as Material).uuid

                if (meshCollect[uuid]) {
                    meshCollect[uuid].push(mesh)
                } else {
                    meshCollect[uuid] = [mesh]
                }
            } else {
                obj.position.set(0, 0, 0);
                obj.scale.set(1, 1, 1);
                obj.rotation.set(0, 0, 0);
            }

        })

        // create all instances
        Object.keys(meshCollect).forEach(uuid => {
            const meshes = meshCollect[uuid]
            const mesh = meshes[0]
            const instancedMesh = new InstancedMesh(mesh.geometry, mesh.material, meshes.length * count)
            instancedMesh.name = mesh.name;
            instancedMesh.frustumCulled = false;
            instanceCollect[uuid] = instancedMesh
            this.add(instancedMesh)
        })
    }


    // 更新包围盒
    computeBoundingSphere = throttle(() => {
        this.traverse(m => {
            (m as InstancedMesh).computeBoundingSphere();
        })
    }, 1000)

    setMatrixAt(index: number, matrix: Matrix4) {
        Object.keys(this.meshCollect).forEach(uuid => {
            const instancedMesh = this.instanceCollect[uuid]
            const collect = this.meshCollect[uuid]
            collect.forEach((mesh: Mesh, i: number) => {
                InstancedGroupMesh.matrix.copy(mesh.matrix)
                InstancedGroupMesh.matrix.premultiply(matrix)

                instancedMesh.setMatrixAt(collect.length * index + i, InstancedGroupMesh.matrix);
                instancedMesh.instanceMatrix.needsUpdate = true;
            })
        })

        this.computeBoundingSphere();
    }

    // 设置颜色
    setColorAt(index: number, color: (instancedMesh: InstancedMesh) => Color | undefined) {
        Object.keys(this.meshCollect).forEach(uuid => {
            const instancedMesh = this.instanceCollect[uuid]
            const collect = this.meshCollect[uuid]
            collect.forEach((mesh: Mesh, i: number) => {
                const c = color(instancedMesh);
                if (c) {
                    instancedMesh.setColorAt(collect.length * index + i, c);
                    instancedMesh.instanceColor!.needsUpdate = true;
                }
            })
        })
    }

}